﻿using LiteBlog.IoC;
using LiteBlog.Repositories.Common;
using LiteBlog.Repositories.Contracts;
using LiteBlog.Repositories.Framework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace LiteBlog.Repositories.Tests
{
    [TestClass]
    public abstract class StandardRepositoryTests<T, U> : RepositoryTests<U>
        where U : StandardRepository<T>
    {
        protected string _getId = "t";
        protected string _createId = "t2";
        protected string _updateId = "t";
        protected string _deleteId = "t";
        protected string _invalidId = "invalidId";

        protected XElement _createElem;
        protected XElement _updateElem;

        [TestMethod]
        public void DataComponentIsNotNull()
        {
            Assert.IsNotNull(_repository);
        }

        [TestMethod]
        public void GetWorks()
        {
            var list = _repository.Get();
            Assert.IsNotNull(list);
            Assert.IsTrue(list.Any());
        }

        [TestMethod]
        public void GetByIdWorks()
        {
            var item = _repository.Get(_getId);
            VerifyGet(item);
        }

        [TestMethod]
        public void CreateWorks()
        {
            var item = GetObject(_createId);
            _repository.Create(item);
            _mock.Verify(x =>
                x.Save(It.Is<XElement>(elem => VerifyCreate(elem)),
                       It.Is<string>(p => p.Equals(_repository.Path))));
        }

        [TestMethod]
        public void CreateDetectsInvalidInput()
        {
            T item = GetObject(_getId);
            _repository.Create(item);
            _mock.Verify(x => x.Save(
                   It.IsAny<XElement>(),
                   It.IsAny<string>()
               ), Times.Never);
        }

        [TestMethod]
        public void UpdateWorks()
        {
            T item = GetObject(_updateId);
            _repository.Update(_updateId, item);
            _mock.Verify(x =>
                x.Save(It.Is<XElement>(elem => VerifyUpdate(elem)),
                       It.Is<string>(p => p.Equals(_repository.Path))));
        }

        [TestMethod]
        public void UpdateDetectsInvalidInput()
        {
            T item = GetObject(_createId);
            _repository.Update(_createId, item);
            _mock.Verify(x => x.Save(
                   It.IsAny<XElement>(),
                   It.IsAny<string>()
               ), Times.Never);
        }

        [TestMethod]
        public void DeleteWorks()
        {
            _repository.Delete(_deleteId);
            _mock.Verify(x =>
                x.Save(It.Is<XElement>(elem => VerifyDelete(elem)),
                       It.Is<string>(p => p.Equals(_repository.Path))
                ));
        }

        [TestMethod]
        public void DeleteDetectsInvalidInput()
        {
            _repository.Delete(_invalidId);
            _mock.Verify(x => x.Save(
                   It.IsAny<XElement>(),
                   It.IsAny<string>()
               ), Times.Never);
        }

        //protected virtual void VerifyGet(T item)
        //{
        //    string actualId = _repositoryHelper.GetId(item);
        //    Assert.AreEqual(_getId, actualId);
        //}

        //protected virtual bool VerifyCreate(XElement elem)
        //{
        //    var childElems = elem.Elements(_data.ElementName);
        //    _createElem = childElems.SingleOrDefault(e =>
        //        ((string)e.Attribute(_data.IdName)).Equals(_createId));
        //    return _createElem != null;
        //}

        //protected virtual bool VerifyUpdate(XElement elem)
        //{
        //    var childElems = elem.Elements(_data.ElementName);
        //    _updateElem = childElems.SingleOrDefault(e =>
        //        ((string)e.Attribute(_data.IdName)).Equals(_updateId));
        //    return _updateElem != null;
        //}

        //protected virtual bool VerifyDelete(XElement elem)
        //{
        //    return !elem.Elements(_data.ElementName)
        //                 .Any(innerElem =>
        //                     innerElem.Attribute(_data.IdName)
        //                              .Value
        //                              .Equals(_deleteId));
        //}

        protected abstract T GetObject(string id);
        protected abstract bool VerifyGet(T item);
        protected abstract bool VerifyCreate(XElement elem);
        protected abstract bool VerifyUpdate(XElement elem);
        protected abstract bool VerifyDelete(XElement elem);
    }
}
